﻿namespace Mercury.ParticleEngine
{
    using System;
    using System.Runtime.Serialization;
    using FluentAssertions;
    using Xunit;

    public class AxisAngleTests
    {
        public class Constructor
        {
            [Fact]
            public void WhenGivenAxis_InitializesInstanceWithAxis()
            {
                var subject = new AxisAngle(Axis.Up, new RadianAngle(0f));

                subject.Axis.Should().Be(Axis.Up);
            }

            [Fact]
            public void WhenGivenAngle_InitializesNewInstanceWithAngle()
            {
                var subject = new AxisAngle(Axis.Up, new RadianAngle(1f));

                subject.Angle.Should().Be(new RadianAngle(1f));
            }
        }

        public class DeserializationConstructor
        {
            [Fact]
            public void WhenGivenNullSerializationInfo_ThrowsArgumentNullException()
            {
                Action invocation = () => new AxisAngle(null, new StreamingContext());

                invocation.ShouldThrow<ArgumentNullException>();
            }

            [Fact]
            public void WhenGivenSerializationInfo_ReturnsInitializesAxisAngle()
            {
                var serializationInfo = new SerializationInfo(typeof(AxisAngle), new FormatterConverter());

                serializationInfo.AddValue("axis", Axis.Up);
                serializationInfo.AddValue("angle", new RadianAngle(1f));

                var subject = new AxisAngle(serializationInfo, new StreamingContext());

                subject.Axis.Should().Be(Axis.Up);
                subject.Angle.Should().Be(new RadianAngle(1f));
            }
        }

        public class AxisProperty
        {
            [Fact]
            public void ReturnsAxis()
            {
                var subject = new AxisAngle(Axis.Up, new RadianAngle(1f));

                subject.Axis.Should().Be(Axis.Up);
            }
        }

        public class AngleProperty
        {
            [Fact]
            public void ReturnsAngle()
            {
                var subject = new AxisAngle(Axis.Up, new RadianAngle(1f));

                subject.Angle.Should().Be(new RadianAngle(1f));
            }
        }

        public class EqualsMethod
        {
            [Fact]
            public void WhenGivenNull_ReturnsFalse()
            {
                var subject = new AxisAngle(Axis.Up, new RadianAngle(1f));

                subject.Equals(null).Should().BeFalse();
            }

            [Fact]
            public void WhenGivenObjectOfAnotherType_ReturnsFalse()
            {
                var subject = new AxisAngle(Axis.Up, new RadianAngle(1f));

                subject.Equals(DateTimeOffset.UtcNow).Should().BeFalse();
            }

            [Fact]
            public void WhenGivenUnequalValueAsObject_ReturnsFalse()
            {
                var subject = new AxisAngle(Axis.Up, new RadianAngle(1f));
                var other = new AxisAngle(Axis.Down, new RadianAngle(1f));

                subject.Equals(other as Object).Should().BeFalse();
            }

            [Fact]
            public void WhenGivenEqualValueAsObject_ReturnsTrue()
            {
                var subject = new AxisAngle(Axis.Up, new RadianAngle(1f));
                var other = new AxisAngle(Axis.Up, new RadianAngle(1f));

                subject.Equals(other as Object).Should().BeTrue();
            }

            [Fact]
            public void WhenGivenUnequalValue_ReturnsFalse()
            {
                var subject = new AxisAngle(Axis.Up, new RadianAngle(1f));
                var other = new AxisAngle(Axis.Down, new RadianAngle(1f));

                subject.Equals(other).Should().BeFalse();
            }

            [Fact]
            public void WhenGivenEqualValue_ReturnsTrue()
            {
                var subject = new AxisAngle(Axis.Up, new RadianAngle(1f));
                var other = new AxisAngle(Axis.Up, new RadianAngle(1f));

                subject.Equals(other).Should().BeTrue();
            }
        }

        public class GetHashCodeMethod
        {
            [Fact]
            public void WhenValuesAreDifferent_YieldsDistinctHashCodes()
            {
                var x = new AxisAngle(Axis.Up, new RadianAngle(1f));
                var y = new AxisAngle(Axis.Down, new RadianAngle(1f));

                x.GetHashCode().Should().NotBe(y.GetHashCode());
            }

            [Fact]
            public void WhenValuesAreIdentical_YieldsSameHashCode()
            {
                var x = new AxisAngle(Axis.Up, new RadianAngle(1f));
                var y = new AxisAngle(Axis.Up, new RadianAngle(1f));

                x.GetHashCode().Should().Be(y.GetHashCode());
            }
        }

        public class ToStringMethod
        {
            [Fact]
            public void ReturnsStringRepresentation()
            {
                var subject = new AxisAngle(Axis.Up, new RadianAngle(1f));

                subject.ToString().Should().Be("([0.0000, 0.0000, 1.0000], 1.0000)");
            }
        }

        public class GetObjectDataMethod
        {
            [Fact]
            public void WhenGivenSerializationInfo_PopulatesIt()
            {
                var serializationInfo = new SerializationInfo(typeof(AxisAngle), new FormatterConverter());

                ISerializable item = new AxisAngle(Axis.Up, new RadianAngle(1f));

                item.GetObjectData(serializationInfo, new StreamingContext());

                serializationInfo.MemberCount.Should().Be(2);
                serializationInfo.GetValue("axis", typeof(Axis)).Should().Be(Axis.Up);
                serializationInfo.GetValue("angle", typeof(RadianAngle)).Should().Be(new RadianAngle(1f));
            }
        }
    }
}